local skynet = require "skynet"
local cjson = require "cjson"
local datacenter = require "skynet.datacenter"
local http      = require("http.httpc")

local ROOMID = ...
local CMD    = {}

require "functions"

local users = {}

local last_offline_time

local is_dismissing = false

local runfork = true

local game_status = 0 --[[游戏状态 0未开始 1开始]]

local comment_msg = {}
local like_msg = {}
local gift_msg = {}

local page_num = 1
local page_size = 20

local function send2user(uid, pack)
    local u = users[uid]
    if u.agent then
        local ok = pcall(skynet.send, u.agent, "lua", "nativesend", pack)
    end
end

local function send2all(pack)
    for uid, u in pairs(users) do
        if u.agent then
            local ok = pcall(skynet.send, u.agent, "lua", "nativesend", pack)
        end
    end
end

local function start_task(access_token, msg_type)
    local host = "https://webcast.bytedance.com"
    local url = "/api/live_data/task/start"
    local header = {
        ["access-token"] = access_token,
        ["content-type"] = "application/json"
    }
    local args = {
        roomid = ROOMID,
        appid = APPID,
        msg_type = msg_type
    }
    local statuscode, resp_str = http.request("POST", host, url, {}, header, cjson.encode(args))
    skynet.error("start_task response = ", resp_str)
    local ok, resp = pcall(cjson.decode, resp_str)
    if ok then
        if resp.err_no == 0 then
            skynet.error("start_task success msg_type = ", msg_type)
            return
        end
    end
    assert(false, "运行到这里就错了~~~")
end

local function stop_task(access_token, msg_type)
    local host = "https://webcast.bytedance.com"
    local url = "/api/live_data/task/stop"
    local header = {
        ["access-token"] = access_token,
        ["content-type"] = "application/json"
    }
    local args = {
        roomid = ROOMID,
        appid = APPID,
        msg_type = msg_type
    }
    local statuscode, resp_str = http.request("POST", host, url, {}, header, cjson.encode(args))
    skynet.error("stop_task response = ", resp_str)
    local ok, resp = pcall(cjson.decode, resp_str)
    if ok then
        if resp.err_no == 0 then
            skynet.error("stop_task success msg_type = ", msg_type)
            return
        end
    end
    assert(false, "运行到这里就错了~~~")
end


local function check_task(access_token, msg_type)
    local host = "https://webcast.bytedance.com"
    local url = "/api/live_data/task/get"
    local header = {
        ["access-token"] = access_token,
        ["content-type"] = "application/json"
    }
    local args = {
        roomid = ROOMID,
        appid = APPID,
        msg_type = msg_type
    }
    local statuscode, resp_str = http.request("POST", host, url, {}, header, cjson.encode(args))
    skynet.error("check_task response = ", resp_str)
    local ok, resp = pcall(cjson.decode, resp_str)
    if ok then
        if resp.err_no == 0 then
            local status = resp.data.status -- 1 任务不存在 2任务未启动 3任务运行中
            if status ~= 3 then
                if game_status == 1 then
                    start_task(access_token, msg_type)
                end
            end
        end
    end
    assert(false, "运行到这里就错了~~~")
end


local function get_fail_data(access_token, msg_type)
    local host = "https://webcast.bytedance.com"
    local url = "/api/live_data/task/fail_data/get"
    local header = {
        ["access-token"] = access_token,
        ["content-type"] = "application/json"
    }
    local args = {
        roomid = ROOMID,
        appid = APPID,
        msg_type = msg_type,
        page_num = page_num,
        page_size = page_size,
    }
    local statuscode, resp_str = http.request("POST", host, url, {}, header, cjson.encode(args))
    skynet.error("get_fail_data response = ", resp_str)
    local ok, resp = pcall(cjson.decode, resp_str)
    if ok then
        if resp.err_no == 0 then
            local data_list = resp.data.data_list
            if #data_list >= page_size then
                for _,v in ipairs(data_list) do
                    local msg = cjson.decode(v.payload)
                    msg.roomid = v.roomid
                    msg.msg_type = v.msg_type
                    if not gift_msg[msg.msg_id] then
                        gift_msg[msg.msg_id] = msg
                    end
                end
                page_num = page_num + 1
            end
        end
    end
end

--解散房间
local function dismiss()
    skynet.error("解散房间", ROOMID)
    pcall(skynet.send, ROOMMGR, "lua", "dismiss",ROOMID)

    for uid, _ in pairs(users) do
        send2user(uid, { c = "game", m = "dismiss", data = {} })
    end
    skynet.exit()
end


local function usercount()
    local c = 0
    for _, v in pairs(users) do
        c = c + 1
    end
    return c
end

--加入房间
function CMD.join(agent, uid)
    skynet.error("user ", uid ," join room ")
    if is_dismissing then
        send2user(uid, { c = "game", m = "join", data = { errcode = 10001, errmsg = "加入房间失败" } })
        return "ng"
    end
    if users[uid] and users[uid].agent then
        send2user(uid, { c = "game", m = "join", data = { errcode = 10002, errmsg = "请勿重复加入房间" } })
        return "ng"
    end

    users[uid] = { agent = agent, uid = uid}
    last_offline_time = nil

    send2user(uid, { c = "game", m = "join", data = { service = skynet.self(), roomid = ROOMID } })
    return "ok"
end

function CMD.start_task()
    local ok, access_token = pcall(skynet.call, "cache", "lua", "getAccessToken", APPID, SECRET)
    skynet.error("start_task ok = ", ok)
    skynet.error("start_task access_token = ", access_token)
    if not ok then
        send2user(uid, { c = "game", m = "start", data = { errcode = 10001, errmsg = "任务启动失败" } })
        return 'ng'
    end
    start_task(access_token, "live_comment")
    start_task(access_token, "live_gift")
    start_task(access_token, "live_like")
    send2user(uid, { c = "game", m = "start", data = { errcode = 0, errmsg = "" }  })
    game_status = 1
    return "ok"
end

function CMD.stop_task()
    local ok, access_token = pcall(skynet.call, "cache", "lua", "getAccessToken", APPID, SECRET)
    skynet.error("stop_task ok = ", ok)
    skynet.error("stop_task access_token = ", access_token)
    stop_task(access_token, "live_comment")
    stop_task(access_token, "live_gift")
    stop_task(access_token, "live_like")
    game_status = 0
    return "ok"
end

--离线
function CMD.offline(uid)
    skynet.error("user offline uid = ", uid)
    local u = users[uid]
    if u then
        skynet.error("user offline offline_time = ",  os.time())
        u.agent = nil
        u.offline_time = os.time()
    end
    last_offline_time = os.time()

    return "ok"
end

--离线
function CMD.leave(uid)
    skynet.error("user leave uid = ", uid)
    if not users[uid] then
        return
    end
    users[uid] = nil
    last_offline_time = os.time()
    return "ok"
end

function CMD.sendmsg(msg_type, msg)
    if game_status == 0 then
        skynet.error("游戏任务未启动 = ")
        return "ng"
    end
    skynet.error("room sendmsg msg_type = ", msg_type, " msg = ", cjson.encode(msg))
    if msg_type == "live_comment" then
        local msg_id = msg.msg_id
        if comment_msg[msg_id] then
            skynet.error("comment_msg exsit msg_id = ", msg_id)
            return "ng"
        else
            msg.msg_type = msg_type
            comment_msg[msg_id] = msg
        end
    elseif msg_type == "live_like" then
        local msg_id = msg.msg_id
        if like_msg[msg_id] then
            skynet.error("like_msg exsit msg_id = ", msg_id)
            return "ng"
        else
            msg.msg_type = msg_type
            like_msg[msg_id] = msg
        end
    elseif msg_type == "live_gift" then
        local msg_id = msg.msg_id
        if gift_msg[msg_id] then
            skynet.error("gift_msg exsit msg_id = ", msg_id)
            return "ng"
        else
            msg.msg_type = msg_type
            gift_msg[msg_id] = msg
        end
    else
        skynet.error("不能运行到这里~~~", " msg_type = ", msg_type)
        return "ng"
    end
    return "ok"
end


local function forward_common_message()
    for msg_id, msg in pairs(comment_msg) do
        send2user(uid, { c = "game", m = "sendmsg", data = { errcode = 0, errmsg = "", data = {msg = msg} }  })
        comment_msg[msg_id] = nil
    end
end


local function forward_like_message()
    for msg_id, msg in pairs(like_msg) do
        send2user(uid, { c = "game", m = "sendmsg", data = { errcode = 0, errmsg = "", data = {msg = msg} }  })
        like_msg[msg_id] = nil
    end
end

local function forward_gift_message()
    for msg_id, msg in pairs(gift_msg) do
        send2user(uid, { c = "game", m = "sendmsg", data = { errcode = 0, errmsg = "", data = {msg = msg} }  })
        gift_msg[msg_id] = nil
    end
end


function CMD.start(room_conf, roommgr)
    skynet.error("开启房间", ROOMID)

    ROOM_CNF = room_conf

    ROOMMGR = roommgr

    --房间在线人数检测
    skynet.fork(function()

        while runfork do
            skynet.error(" 房间在线人数检测 ")
            for uid, u in pairs(users) do
                if u.offline_time and os.time() - u.offline_time > 30 then
                    skynet.error("用户 销毁 = ", uid)
                    send2all({ c = "game", m = "leave", data = { uid = uid } })
                    users[uid] = nil
                end
            end

            if last_offline_time then
                local elapsed = os.time() - last_offline_time

                --超过3分钟，如果房间没有人，关闭房间
                if elapsed >= 60 then
                    runfork = false
                    is_dismissing = true
                    game_status = 0
                    --解散房间
                    dismiss()
                    break
                end
            end
            skynet.sleep(5 * 100)
        end
    end)

    --[[
        分页查询推送失败数据(仅支持礼物数据)
        注意：补偿数据的查询没有 ack 机制，需要开发者自行维护补偿状态，比如当前补偿到第几页了，下次该读取第几页
    ]]
    skynet.fork(function()
        while runfork do
            if game_status == 1 then
                local ok, access_token = pcall(skynet.call, "cache", "lua", "getAccessToken", APPID, SECRET)
                skynet.error("get_fail_data ok = ", ok)
                skynet.error("get_fail_data access_token = ", access_token)
                get_fail_data(access_token, "live_comment")
            end
            skynet.sleep(3 * 100)
        end
    end)

    --发送消息给前端
    skynet.fork(function()
        while runfork do
            if game_status == 1 then
                forward_common_message()
                forward_like_message()
                forward_gift_message()
            end
            skynet.sleep(10)
        end
    end)

    --发送消息给前端
    skynet.fork(function()
        while runfork do
            if game_status == 1 then
                local ok, access_token = pcall(skynet.call, "cache", "lua", "getAccessToken", APPID, SECRET)
                skynet.error("check_task ok = ", ok)
                skynet.error("check_task access_token = ", access_token)
                check_task(access_token, "live_comment")
                check_task(access_token, "live_like")
                check_task(access_token, "live_gift")
            end
            skynet.sleep(30 * 100)
        end
    end)

    return "ok"
end

skynet.start(function()
    skynet.dispatch("lua", function(_, _, command, ...)
        -- skynet.trace()
        local f = CMD[command]
        skynet.ret(skynet.pack(f(...)))
    end)
end)
